﻿using Autofac;
using Autofac.Extensions.DependencyInjection;
using Bean.Infrastructure.Autofac;
using Bean.Infrastructure.AutoMapper;
using Bean.Repository;
using IGeekFan.AspNetCore.Knife4jUI;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using NLog.Extensions.Logging;
using Renci.SshNet;

namespace Bean.Infrastructure.Extensions
{
    /// <summary>
    /// 
    /// </summary>
    public static class AppStartupExtenison
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static WebApplicationBuilder AddCommonService(this WebApplicationBuilder builder,
            string sectionName = "Nlog.Config")
        {
            builder.Services.AddControllers(option =>
            {
                option.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;
            }).AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                options.SerializerSettings.ContractResolver = new NullToEmptyStringValueResolver();
            });
            builder.Services.AddHttpClient();
            builder.Services.AddHttpContextAccessor();
            builder.Services.AddLogging(m => m.AddNLog(Path.Combine(AppContext.BaseDirectory, sectionName)));
            builder.Services.AddResponseCaching();
            builder.Services.AddMemoryCache();
            return builder;
        }


        #region Logging

        public static WebApplicationBuilder AddLogging(this WebApplicationBuilder builder,
            string sectionName = "nlog.config")
        {
            builder.Services.AddLogging(m => m.AddNLog(Path.Combine(AppContext.BaseDirectory, sectionName)));
            return builder;
        }

        #endregion


        /// <summary>
        /// redis 缓存以及配置文件
        /// </summary>
        /// <param name="services"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static WebApplicationBuilder AddRedisConfig(this WebApplicationBuilder builder,
            string sectionName = "Database:Redis")
        {
            try
            {
                var redisInfo = builder.Configuration.GetSection(sectionName).Get<RedisDatabase>();
                var redisConnectString = $"{redisInfo.EndPoint}:{redisInfo.Port},password={redisInfo.Password},defaultDatabase={redisInfo.DefaultDatabase}";
                RedisHelper.Initialization(new CSRedis.CSRedisClient(redisConnectString));
                return builder;
            }
            catch (Exception e)
            {
                throw new Exception($"注入Redis 发生未知异常:{e.Message}");
            }
        }


        /// <summary>
        /// redis 缓存以及配置文件
        /// Live环境
        /// </summary>
        /// <param name="services"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static WebApplicationBuilder AddLiveRedisConfig(this WebApplicationBuilder builder,
            string sectionName = "Database:Redis")
        {
            try
            {
                var redisInfo = builder.Configuration.GetSection(sectionName).Get<RedisDatabase>();
                var sshClient = new SshClient(redisInfo.SSH.Host,redisInfo.SSH.Port,redisInfo.SSH.Username,redisInfo.SSH.Password);
                sshClient.Connect();
                Console.WriteLine($"连接成功");
                // 设置端口转发
                var tunnel = new ForwardedPortLocal(6379, "127.0.0.1", 6379);
                sshClient.AddForwardedPort(tunnel);
                tunnel.Start();
                //builder.Configuration.GetSection(sectionName).Value
                //192.168.2.188:6379,password = 123123,defaultDatabase = 0
                var redisEndPoint = $"{redisInfo.EndPoint}:{redisInfo.Port},password={redisInfo.Password},defaultDatabase={redisInfo.DefaultDatabase}";
                RedisHelper.Initialization(new CSRedis.CSRedisClient(redisEndPoint));
                return builder;
            }
            catch (Exception e)
            {
               throw new Exception($"注入Redis 发生未知异常:{e.Message}");
            }
        }


        #region Cors 跨域

        /// <summary>
        /// 添加跨域设置
        /// </summary>
        /// <param name="services"></param>
        /// <param name="policy"></param>
        /// <returns></returns>
        public static WebApplicationBuilder AddMyCors(this WebApplicationBuilder builder, string policy = "ut_cors")
        {
            builder.Services.AddCors(options => options.AddPolicy(policy, builder =>
            {
                builder
                    .SetIsOriginAllowed(_ => true) //为基础策略设置指定的isOriginAllowed
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials();
            }));
            return builder;
        }

        /// <summary>
        /// 引用跨域
        /// </summary>
        /// <param name="app"></param>
        /// <param name="policy"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseMyCors(this IApplicationBuilder app, string policy = "ut_cors")
        {
            app.UseCors(policy);
            return app;
        }

        #endregion


        #region swagger 文档

        /// <summary>
        /// 添加swagger文档
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static WebApplicationBuilder AddSwaggerDoc(this WebApplicationBuilder builder,
            string sectionName = "Swagger", string searchPattern = "*.xml")
        {
            builder.Services.AddSwaggerGen(options =>
            {
                var swaggerInfo = builder.Configuration.GetSection(sectionName).Get<OpenApiInfo>();
                options.SwaggerDoc("v1", swaggerInfo);

                var basePath = AppContext.BaseDirectory;
                foreach (var xmlFilePath in Directory.GetFiles(basePath, searchPattern, SearchOption.TopDirectoryOnly))
                {
                    options.IncludeXmlComments(xmlFilePath, true);
                }
            });
            return builder;
        }

        /// <summary>
        /// 引用swagger
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseSwaggerDoc(this IApplicationBuilder app, string url = "v1/swagger.json")
        {
            app.UseSwagger();
            app.UseKnife4UI(options =>
            {
                var env = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
                options.SwaggerEndpoint(url, env.ApplicationName);
            });
            return app;
        }

        #endregion


        #region SqlSugar

        /// <summary>
        /// 
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static WebApplicationBuilder AddSqlSugar(this WebApplicationBuilder builder,
            string sectionName = "Database:SqlSugarConfig")
        {
            builder.Services.AddSqlSugarClient(builder.Configuration, sectionName);
            return builder;
        }

        #endregion


        #region autofac

        /// <summary>
        /// 
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static WebApplicationBuilder AddAutofac(this WebApplicationBuilder builder, string projectName = "*.dll")
        {
            builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());

            Microsoft.Extensions.Hosting.HostingHostBuilderExtensions.ConfigureContainer<ContainerBuilder>(builder.Host,
                builder =>
                {
                    builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerDependency();

                    builder.RegisterAppService(projectName);
                });

            builder.Services.Replace(
                ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());

            return builder;
        }

        #endregion


        /// <summary>
        /// 注入 AutoMapper
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static WebApplicationBuilder AddAutoMap(this WebApplicationBuilder builder)
        {
            builder.Services.AddAutoMapper(cfg => { cfg.AddProfile<MappingProfile>(); });

            return builder;
        }
    }
}